#include "../include/ThreadCache.h"

#include "../include/CentralCache.h"

#include <cstddef>
#include <cstdlib>

namespace mp {

void *ThreadCache::allocate(size_t size) {
    if (size == 0) {       // 处理0大小的分配请求
        size = ALIGNMENT;  // 至少分配一个对齐大小
    }

    if (size > MAX_BYTES) {
        return malloc(size);  // 大对象直接从系统分配
    }

    size_t idx = SizeClass::getIdx(size);

    --freeListSize_[idx];  // 更新自由链表大小

    // 检查线程本地自由链表
    // 如果 freeList_[index] 不为空，表示该链表中有可用内存块
    if (void *ptr = freeList_[idx]) {
        freeList_[idx] =
            *reinterpret_cast<void **>(ptr);  // 将freeList_[index]指向的内存块的下一个内存块地址（取决于内存块的实现）
        return ptr;
    }

    return fetchFromCentralCache(idx);  // 如果线程本地自由链表为空，则从 CentralCache 获取一批内存
}

void ThreadCache::deallocate(void *ptr, size_t size) {
    if (size > MAX_BYTES) {
        free(ptr);
        return;
    }

    size_t idx = SizeClass::getIdx(size);

    *reinterpret_cast<void **>(ptr) = freeList_[idx];  // 插入到线程本地自由链表
    freeList_[idx] = ptr;

    ++freeListSize_[idx];  // 增加对应大小类的自由链表大小

    if (shouldReturnToCentralCache(idx)) {  // 判断是否需要将部分内存回收给 CentralCache
        returnToCentralCache(freeList_[idx], size);
    }
}

bool ThreadCache::shouldReturnToCentralCache(size_t idx) {
    size_t threshold = 64;  // 设定阈值，例如：当自由链表的大小超过一定数量时
    return freeListSize_[idx] > threshold;
}

void *ThreadCache::fetchFromCentralCache(size_t idx) {
    size_t size = (idx + 1) * ALIGNMENT;
    size_t batchNum = getBatchNum(size);  // 根据对象内存大小计算批量获取的数量
    void *start = CentralCache::getInstance().fetchRange(idx, batchNum);  // 从 CentralCache 批量获取内存
    if (!start)
        return nullptr;

    freeListSize_[idx] += batchNum;  // 更新自由链表大小

    // 取一个返回，其余放入线程本地自由链表
    void *rst = start;
    if (batchNum > 1)
        freeList_[idx] = *reinterpret_cast<void **>(start);

    return rst;
}

void ThreadCache::returnToCentralCache(void *start, size_t size) {
    size_t idx = SizeClass::getIdx(size);           // 根据大小计算对应的索引
    size_t alignedSize = SizeClass::roundUp(size);  // 获取对齐后的实际块大小
    size_t batchNum = freeListSize_[idx];           // 计算要归还的内存块数量
    if (batchNum <= 1)                              // 如果只有一个块，则不归还
        return;

    size_t keepNum = std::max(batchNum / 4, size_t(1));  // 保留一部分在 ThreadCache 中
    size_t returnNum = batchNum - keepNum;

    char *cur = static_cast<char *>(start);  // 将内存块串成链表
    char *splitNode = cur;                   // 使用对齐后的大小计算分割点
    for (size_t i = 0; i < keepNum - 1; ++i) {
        splitNode = reinterpret_cast<char *>(*reinterpret_cast<void **>(splitNode));
        if (splitNode == nullptr) {  // 如果链表提前结束，更新实际的返回数量
            returnNum = batchNum - (i + 1);
            break;
        }
    }

    if (splitNode != nullptr) {
        void *nextNode = *reinterpret_cast<void **>(splitNode);  // 将要返回的部分和要保留的部分断开
        *reinterpret_cast<void **>(splitNode) = nullptr;         // 断开连接

        freeList_[idx] = start;        // 更新 ThreadCache 的空闲链表
        freeListSize_[idx] = keepNum;  // 更新自由链表大小

        if (returnNum > 0 && nextNode != nullptr) {  // 将剩余部分返回给 CentralCache
            CentralCache::getInstance().returnRange(nextNode, returnNum * alignedSize, idx);
        }
    }
}

size_t ThreadCache::getBatchNum(size_t size) {
    constexpr size_t MAX_BATCH_SIZE = 4 * 1024;  // 4KB，基准：每次批量获取内存上限

    /* 根据对象大小设置合理的基准批量数 */
    size_t baseNum;
    if (size <= 32)
        baseNum = 64;  // 64 * 32 = 2KB
    else if (size <= 64)
        baseNum = 32;  // 32 * 64 = 2KB
    else if (size <= 128)
        baseNum = 16;  // 16 * 128 = 2KB
    else if (size <= 256)
        baseNum = 8;  // 8 * 256 = 2KB
    else if (size <= 512)
        baseNum = 4;  // 4 * 512 = 2KB
    else if (size <= 1024)
        baseNum = 2;  // 2 * 1024 = 2KB
    else
        baseNum = 1;  // 大于1024的对象每次只从中心缓存取1个

    size_t maxNum = std::max(size_t(1), MAX_BATCH_SIZE / size);  // 计算最大批量数

    return std::max(sizeof(1), std::min(maxNum, baseNum));  // 取最小值，但确保至少返回 1
}

}  // namespace mp